package licode.unisop.log.client.callinter;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.alibaba.fastjson2.JSON;
import licode.unisop.client.api.SopPlatform;
import licode.unisop.client.api.SopPlatformAuth;
import licode.unisop.client.api.SopPlatformMng;
import licode.unisop.client.config.SopConfig;
import licode.unisop.client.info.SopIdInfo;
import licode.unisop.client.utils.SopRequestParse;
import licode.unisop.client.vo.SopAuthPlatform;
import licode.unisop.log.client.api.CallingLogApi;
import licode.unisop.log.client.dto.CallingLogDTO;
import licode.unisop.types.AuthConst;
import licode.unisop.types.dp.SizeConstDef;
import licode.unisop.types.util.AuStrUtil;
import licode.unisop.unilite.result.UniException;
import licode.unisop.unilite.result.UniResult;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.UUID;

/**
 * 实现系统API切面拦截
 *
 * @author WR
 * @since 2022/7/9 13:47
 */
@Slf4j
@Aspect
@Component
public class ApiCallLogAspect {
    @Resource
    private CallingLogApi callingLogApi;

    @Resource
    protected SopPlatformMng platformMng;

    @Resource
    protected SopPlatformAuth sopPlatformAuth;

    @Value("${uni-sop.log:}")
    private String pointcut;

    private int errorTimes = 0;

    @Pointcut("execution(public * licode.unisop.authcenter..adapter.web..*.*(..))")
    public void adapterLog() {
    }

    @Around("adapterLog()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        ServletRequestAttributes servletRequestAttributes;
        servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
        HttpServletRequest request = servletRequestAttributes.getRequest();
        long startTime = System.currentTimeMillis();

        // 统一日志标记
        String session = request.getHeader("session");
        if (StrUtil.isEmpty(session)) {
            session = UUID.randomUUID().toString();
        }

        if (errorTimes > 3) {
            return joinPoint.proceed(joinPoint.getArgs());
        }

        String reqUrl = request.getRequestURI();
        if (StrUtil.isNotBlank(request.getHeader(AuthConst.SKIP_LOG_RECORD)) ||
                reqUrl.endsWith("auth/renew-duration")) {
            return joinPoint.proceed(joinPoint.getArgs());
        }

        MDC.put("session", session);

        String platformId = "";
        String platformName = "";
        String callMethod = request.getMethod();
        String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        Object[] paramValues = joinPoint.getArgs();

        JSONObject json = new JSONObject();
        SopPlatform platform = platformMng.def();
        platformId = SopRequestParse.getPlatformId(request);
        String clientId = SopRequestParse.getClientId(request);
        String terminalId = SopRequestParse.getTerminalId(request);

        if (StrUtil.isNotBlank(platformId)) {
            SopAuthPlatform authPlat = sopPlatformAuth.fromId(SopIdInfo.builder().id(platformId).build());
            if (authPlat != null) {
                platformId = authPlat.getPlatformId();
                platformName = authPlat.getName();
            }
        } else if (null != platform) {
            platformId = platform.getId();
            platformName = platform.getName();
        }

        if (paramValues != null && paramValues.length > 0) {
            for (int i = 0; i < paramValues.length; i++) {
                if (paramValues[i] instanceof ServletRequest || paramValues[i] instanceof ServletResponse || paramValues[i] instanceof MultipartFile) {
                    continue;
                }
                json.set(paramNames[i], paramValues[i]);
            }
        }

        String strParams;
        String signature = joinPoint.getSignature().getDeclaringTypeName();

        strParams = json.toStringPretty();

        log.debug("request: URL[{}], HTTP Method[{}], Class Method[{}.{}], Param[{}]",
                reqUrl, callMethod, signature,
                joinPoint.getSignature().getName(), strParams);

        CallingLogDTO callApiLog = new CallingLogDTO();

        callApiLog.setCallTime(LocalDateTime.now());
        callApiLog.setParam(getParam(strParams));
        callApiLog.setResult("");
        callApiLog.setType(getType(callMethod));
        callApiLog.setUrl(getUrl(reqUrl));
        callApiLog.setTenantId("");

        callApiLog.setPlatformId(AuStrUtil.getContent(null == platformId ? "" : platformId,
                SizeConstDef.MAX_COMMON_ID));
        callApiLog.setPlatformName(AuStrUtil.getContent(null == platformName ? "" : platformName,
                SizeConstDef.MAX_PLATFORM_NAME));
        callApiLog.setTerminalId(AuStrUtil.getContent(terminalId, SizeConstDef.MAX_COMMON_ID));
        callApiLog.setClientId(AuStrUtil.getContent(clientId, SizeConstDef.MAX_COMMON_ID));

        Exception ecp = null;
        Object obj;

        try {
            obj = joinPoint.proceed(joinPoint.getArgs());
        } catch (UniException e) {
            ecp = e;
            obj = UniResult.of("", e.getCode(), e.getMessage());
        } catch (Exception e) {
            ecp = e;
            obj = UniResult.of("", 500, e.getMessage());
        }

        startTime = System.currentTimeMillis() - startTime;
        callApiLog.setCostTime(startTime);

        if (obj instanceof UniResult) {
            UniResult ur = (UniResult)obj;
            if (ur.getCode() == 0) {
                callApiLog.setResult(AuStrUtil.getContent(null == ur.getData() ? "" : JSON.toJSONString(ur.getData()),
                        SizeConstDef.MAX_API_RESULT));
            } else {
                callApiLog.setResult(AuStrUtil.getContent(JSON.toJSONString(obj), SizeConstDef.MAX_API_RESULT));
            }
        } else {
            callApiLog.setResult(AuStrUtil.getContent(JSON.toJSONString(obj), SizeConstDef.MAX_API_RESULT));
        }

        try {
            callingLogApi.save(callApiLog);
        } catch (Exception e) {
            e.printStackTrace();
            ++errorTimes;
        }

        MDC.remove("ThreadID");

        if (null != ecp) {
            throw ecp;
        }

        return obj;
    }

    private String getParam(String str) {
        return AuStrUtil.getContent(str, SizeConstDef.MAX_API_PARAM);
    }

    private String getUrl(String str) {
        return AuStrUtil.getContent(str, SizeConstDef.MAX_API_URL);
    }

    private String getType(String str) {
        return AuStrUtil.getContent(str, SizeConstDef.MAX_API_TYPE);
    }
}
